package com.tgy.framework.config.aspect;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.tgy.result.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author liu.jihong
 * @version 1.0
 * @date 2021/09/28 14:11
 */
@Aspect
@Component
@Slf4j
public class LogPrintAspect {

    /**
     * 切点路径
     */
    @Pointcut("execution(public * com.tgy.*.controller..*.*(..))"+
            " || execution(public * com.tgy.other.controller..*.*(..))")
    public void logPrint() {
    }

    /**
     * 在切点之前织入
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before("logPrint()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        String msgId = UUID.randomUUID().toString();
        MDC.put("requestId", msgId);
        // 打印请求相关参数
        StringBuffer inputLog = new StringBuffer();
        inputLog.append("\n======================================= Input-Start ======================================");
        inputLog.append("\n请求URL            : {}");
        inputLog.append("\n请求header            : {}");
        inputLog.append("\n请求类型    : {}");
        inputLog.append("\n全路径及方法   : {}.{}");
        inputLog.append("\n请求入参   : {}");
        inputLog.append("\n====================================== Input-End =========================================");

        //遍历头信息
        Enumeration headerNames = request.getHeaderNames();
        Map<String, String> headerMap = new HashMap<>();
        String key;
        String value;
        while (headerNames.hasMoreElements()) {
            key = (String) headerNames.nextElement();
            value = request.getHeader(key);
            headerMap.put(key, value);
        }

        log.info(inputLog.toString(), request.getRequestURL().toString(),
                StringUtils.join(headerMap.entrySet(), ','), request.getMethod(),
                joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
                getParams(joinPoint));
    }

    /**
     * 在切点之后织入
     *
     * @throws Throwable
     */
    @After("logPrint()")
    public void doAfter() throws Throwable {

    }

    /**
     * @throws Throwable
     */
    @AfterThrowing(throwing = "ex", pointcut = "logPrint()")
    public void doAfterThrowing(Throwable ex) {
        long startTime = System.currentTimeMillis();
        StringBuffer inputLog = new StringBuffer();
        inputLog.append("\n======================================= Out-Start ========================================");
        inputLog.append("\n响应参数  : {}");
        inputLog.append("\n异常信息：{}");
        inputLog.append("\n本次请求消耗时间 : {} ms");
        inputLog.append("\n====================================== Out-End ==========================================");
        log.info(inputLog.toString(), JSONUtil.toJsonStr(AjaxResult.error(ex.getMessage())),
                ex.getStackTrace()[0], (System.currentTimeMillis() - startTime));
    }

    /**
     * 环绕
     *
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("logPrint()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        StringBuffer inputLog = new StringBuffer();
        inputLog.append("\n======================================= Out-Start ========================================");
        inputLog.append("\n响应参数  : {}");
        inputLog.append("\n本次请求消耗时间 : {} ms");
        inputLog.append("\n====================================== Out-End ==========================================");
        log.info(inputLog.toString(), JSONObject.toJSONString(result),
                (System.currentTimeMillis() - startTime));
        return result;
    }

    private String getParams(JoinPoint joinPoint) {
        String params = "";
        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
                Object arg = joinPoint.getArgs()[i];
                if ((arg instanceof HttpServletResponse) || (arg instanceof HttpServletRequest)
                        || (arg instanceof MultipartFile) || (arg instanceof MultipartFile[])) {
                    continue;
                }
                try {
                    params += JSONObject.toJSONString(joinPoint.getArgs()[i]);
                } catch (Exception e1) {
                    log.error(e1.getMessage());
                }
            }
        }
        return params;
    }

}

